设计模式之禅
[TOC]
第二部分 23种设计模式- 行为类模式
行为类模式: 描述类和对象之间如何交互及如何分配职责
一.策略模式
1.定义
定义一组算法,将每个算法都封装起来,并使得它们可以互相转换
2.类图
3.角色
- Context 封装角色
上下文角色,对策略进行封装,屏蔽高层模块对策略算法的直接访问 - Strategy抽象策略角色
通常为接口,定义每个策略算法必须具有的方法和属性 - ConcreteStrategy具体策略角色
实现抽象策略中的操作
相比代理模式
策略模式中封装角色(代理角色) 与 被封装的策略类(原角色) 不是用同一个接口
4.优点
- 算法可以自由切换
- 避免使用多重条件判断
- 扩展性良好
5.缺点
- 策略类数量增多
- 所有策略类都要对外暴露
二.迭代器模式
1.定义
它提供一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节
目前已经没落的模式
2.类图
3.角色
Iterator抽象迭代器
ConcreteIterator具体迭代器
- Aggregate抽象容器
- ConcreteAggregate具体容器
4.应用
java.util.Iterable接口( 只有一个方法: iterator() ) 已经应用到了各个集合类中。基本很少有项目再独立写迭代器,直接使用Collection下的实现类就可以解决问题。迭代器的使用将访问逻辑与数据结构分离开来,不同的数据结构都可以用同样的逻辑来读取这些数据
三.模版方法模式
1.定义
定义一个操作中的算法的框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤
2.类图
3.角色
AbstractClass 抽象模版,方法分为两类:
- 基本方法(基本操作),需由子类实现的方法,在模版方法时被调用
- 模版方法,一般是一个或几个,一般是一个具体方法,实现对基本方法的调度
为了防止恶意操作,一般模版方法都加上final关键字,不允许被覆写
ConcreteClass1、ConcreteClass2 具体模版
实现父类所定义的一个或多个抽象方法,也就是父类定义的基本方法在子类中得以实现
4.优点
- 封装不变部分,扩展可变部分
- 提取公共部分代码,便于维护
- 行为由父类控制,子类实现
5.扩展
使用钩子函数,通过标识/子类的一个方法返回值决定公共部分的执行结果
四.中介者模式
1.定义
使各对象不需要显示地互相作用,从而使器耦合松散,独立地改变它们之间的交互
2.类图
3.角色
Mediator 抽象中介者角色
用于各同事角色之间的通信Concrete Mediator 具体中介者角色
协调各同事角色实现协作行为,因此依赖于各个同事角色Colleague 同事角色
每一个同事角色都知道中介者角色。行为分为同事本身的自发行为、依赖中介者的依赖行为
通用代码见设计模式之禅P174
4.应用
优点: 减少类间的依赖,降低耦合
缺点: 中介者会膨胀得很大,逻辑复杂
使用场景: 类图中出现了蜘蛛网结构,中介者模式有利于把其梳理为星型结构
实际应用
- MVC框架,C(Controller)就是一个中介者,作用就是将M(Model 业务逻辑)和V(View 视图)隔离开,协调M和V工作
- 媒体网关
五.命令模式
1.定义
将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能
2.类图
3.角色
Receive 接受者角色
干活的角色,命令传递到该角色执行Command 命令角色
需要执行的命令都在这声明Invoker 调用者角色
接收命令,并执行命令
4.应用
优点: 类间解耦、可扩展性、
缺点: Command的子类可能会膨胀得很大
5.实践
- 当客户反悔了,需要回滚。有两种方法,一 结合备忘录模式还原最后状态,二 新增一个反命令。在接受者中添加rollback方法
- 命令类的Receiver在实际应用中一般会被封装掉
六.责任链模式
1.定义
使多个对象都有机会处理请求,从而避免了请求发送者和接受者之间的耦合关系,将这些对象连成一条链,沿着这条链传递该请求,直到有对象处理它为止
2.类图
3.角色
- Handler 抽象处理者—抽象类
- ConcreteHandler 具体实现类
在处理中涉及了三个类
场景类
在实际应用中,会有封装类对具体链的设置进行封装,简化高层次模块的调用,减少模块间的耦合
4.应用
优点: 将请求和处理分开
缺点: 性能问题,当链特别长时,调试不方便
5.实践
例子中的Handler时抽象类,融合了模版方法。每个实现类只需要关心echo方法处理请求,getHandlerLevel获得处理级别
七.观察者模式(发布订阅模式)
1. 定义
定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新
2. 类图
3. 角色
- Subject 被观察者
定义被观察者必须实现的职责,能够动态增加、取消观察者 - Observer 观察者
观察者接收到消息,执行自己的业务逻辑update - ConcreteSubject 具体的被观察者
- ConcreteObserver 具体的观察者
4. 应用
优点: 观察者与被观察者对象是抽象耦合的、建立一套触发机制
缺点: 注意多级触发时的效率
根据经验,一个观察者模式中最多出现一个对象既是观察者也是被观察者,也就是说消息最多被转发一次
util包提供了Observer、Observeble接口使用
八.备忘录模式
1. 定义
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以后可将该对象恢复到原先保存的状态
2. 类图
3. 角色
- Originator 发起人角色
记录当前时刻的内部状态,负责定义哪些属性需要备份,创建、恢复备忘录数据 - Memento 备忘录角色
负责存储Originator 对象的内部状态 - Caretaker 备忘录管理员角色
对备忘录进行管理,保存和提供备忘录
4. 应用
使用场景
- 需要保存及恢复数据
- 提供回滚操作
注意事项
- 备忘录的生命期
- 备忘录的性能
5. 扩展
- clone方式创建备忘录
通过让发起人实现Clonable接口 - 多状态的备忘录模式(这一部分的事例代码看的有点点不懂)
- 多备份备忘录
存储备忘录通过容器类 - 安全性封装
类图如下:
外部只保留一个空接口,数据存储在Caretaker,当要恢复取得对象时,只有在发起人类中才能将拿到的对象,转换成正确的类型
九.访问者模式
1. 定义
封装一些作用于某种数据结构中的各元素的操作,在不改变数据结构的前提下定义作用于这些元素的新操作
2. 类图
3. 角色
Visitor 抽象访问者
抽象类或接口,声明访问者可以访问哪些元素
ConcreteVisitor 具体访问者
Element 抽象元素
抽象类或接口,声明接受哪一类访问者访问。通过accept方法的参数定义- ConcreteElement 具体元素
- ObjectStruture 结构对象
元素生产者
4. 应用
优点
- 符合单一职责原则
- 优秀的扩展性
- 灵活性非常高
缺点
- 具体元素对访问者公布细节
- 具体元素的变更困难
- 违背了依赖倒置原则,访问者依赖具体元素,不是接口
访问者模式是对迭代器模式的扩充
十.状态模式
1. 定义
当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类
2. 类图
3. 角色
- State 抽象状态角色
接口或抽象类,负责对象状态定义,封装环境角色实现状态切换
定义了每个状态下的方法
- ConcreteState 具体状态角色
每个具体状态需要完成的两个职责:
- 本状态的行为管理
- 本状态过渡到其他状态
具体状态下,对对应方法的实现,及切换状态(改变Context中的状态角色)
- Context 环境角色
定义客户端需要的接口,并且负责具体状态的切换
不成文约束:
- 把状态对象声明为静态常量,有几个状态就声明几个
- 环境角色具有状态抽象角色的所有行为。具体执行使用委托方式
附: 场景类
4. 应用
工作流
优点
- 结构清晰
- 遵循设计原则
- 封装性好
缺点
: 子类太多,会造成类膨胀
使用场景
: 行为随状态改变而改变,条件分支判断语句的替代者
十一.解释器模式
按照一种规定语法进行解析的方案,在项目中使用的较少
1.定义
给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子
不详细写